-
Notifications
You must be signed in to change notification settings - Fork 41
Use Microsoft feature flag schema and remove use of reflection for json parsing #556
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use Microsoft feature flag schema and remove use of reflection for json parsing #556
Conversation
…otnetProvider into ajusupovic/use-microsoft-featureflag-schema
|
I'll be adding more unit tests |
| ""variant"": ""Big"", | ||
| ""users"": [ | ||
| ""Marsha"", | ||
| ""John"" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indentation inconsistent throughout this file. Might be worth running dotnet format on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tried to do this now, looks like it doesn't work the same with strings within code unless I missed something. I could try to fix it manually too, just not sure if there's a better way
| expectedJsonValueKind)); | ||
| } | ||
|
|
||
| private FeatureFlag ParseFeatureFlag(string settingKey, string settingValue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like us having to do this manually. Did we look into source generation as an alternative? https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/reflection-vs-source-generation?pivots=dotnet-8-0#reflection
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fair, it does require a lot more testing and is more code to maintain. One reason to continue with this approach is it follows the pattern we established in our other .NET repos of always using Utf8JsonReader for reading Json, which is more efficient compared to the JsonSerializer.Deserialize method. Also, we already added it to the main branch without variants/allocation/telemetry and this PR is re-adding it for merge conflict purposes.
I'm not sure, but from what I can tell here it looks like source generation isn't available in all of the versions we support, since it's only for C# 9.0 and later, and .NET 6.0 and later.
src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Constants/ErrorMessages.cs
Outdated
Show resolved
Hide resolved
...ns.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs
Outdated
Show resolved
Hide resolved
...ns.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs
Outdated
Show resolved
Hide resolved
...ns.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs
Outdated
Show resolved
Hide resolved
...ns.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs
Show resolved
Hide resolved
|
|
||
| string userAllocationPropertyName = reader.GetString(); | ||
|
|
||
| switch (userAllocationPropertyName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we handle the case that one of variant and users is missing? For example,
"allocation": {
"user": [
{
"users": ["a","b"]
}
]
}This user allocation will not take effect. Do we still need to put it in the configuration or throw exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question, I think it should check to see if they aren't empty, so if there's anything inside of "user", either a list of users or a variant name, then it would be added. I thought since we shouldn't necessarily be doing any feature management logic, it would make sense to just exclude it if it's completely empty, but otherwise add the object. So in this case that you put up, I think it should be added. I don't think we need to throw an exception though, since we never did something like that if conditions was empty, for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree with Amer here. Feature Management library is responsible for checking the validity of a feature flag. Provider library just reads and flattens whatever is stored in AppConfig.
…onstants/ErrorMessages.cs Co-authored-by: Zhiyuan Liang <141655842+zhiyuanliang-ms@users.noreply.github.com>
| if (featureManagementVersion != null) | ||
| { | ||
| // If the version is less than 3.2.0, log the schema version warning | ||
| if (featureManagementVersion < new Version(3, 2, 0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Min version could be a constant variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't actually make it a constant because the value is new Version(), but I could just put FeatureManagementMinimumVersion as a readonly class variable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was imagining a const string "3.2.0" which is local to this method. And we use Version.Parse to compare versions.
...ensions.Configuration.AzureAppConfiguration/FeatureManagement/FeaturePercentileAllocation.cs
Outdated
Show resolved
Hide resolved
|
|
||
| [JsonPropertyName("from")] | ||
| public double From { get; set; } | ||
| public double? From { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From and To are required fields. Why change to nullable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh good point, I was thinking to do that so we could tell if they were set later on in the code when the percentile object is added to the key values output, but I don't think there's a point in doing that anyway. They should just be 0 by default like it was before. Regardless it should be wrong to be nullable if they're required
|
|
||
| bool NeedsRefresh(); | ||
|
|
||
| void ResetState(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This interface is accumulating too many methods that apply only to a particular implementation. I'm wondering if we should repurpose the InvalidateCache method instead of introducing a new one. We could rename it to something like ResetInternalState(ConfigurationSetting setting = null) so that it fits both use cases. In all situations, we call this method before processing adapters.
cc @jimmyca15
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you're right that at least something should change here - having the new method seems weird since it's just for this one case. I think a rename works based on where we're calling InvalidateCache now and where the state needs to reset for the FeatureManagementKeyValueAdapter anyway.
src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Constants/LoggingConstants.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Constants/LoggingConstants.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs
Outdated
Show resolved
Hide resolved
| if (featureManagementVersion != null) | ||
| { | ||
| // If the version is less than 3.2.0, log the schema version warning | ||
| if (featureManagementVersion < new Version(3, 2, 0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was imagining a const string "3.2.0" which is local to this method. And we use Version.Parse to compare versions.
...tensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementConstants.cs
Show resolved
Hide resolved
...ns.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs
Show resolved
Hide resolved
...ns.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs
Show resolved
Hide resolved
…onstants/LoggingConstants.cs Co-authored-by: Avani Gupta <avanigupta@users.noreply.github.com>
…onstants/LoggingConstants.cs Co-authored-by: Avani Gupta <avanigupta@users.noreply.github.com>
...ns.Configuration.AzureAppConfiguration/FeatureManagement/FeatureManagementKeyValueAdapter.cs
Show resolved
Hide resolved
src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs
Outdated
Show resolved
Hide resolved
| void OnConfigurationRefresh(ConfigurationSetting setting = null); | ||
|
|
||
| void OnConfigurationUpdated(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about OnChangeDetected and OnConfigUpdated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I think that's clearer for the first one
Emits the feature flag schema defined here.
Added to avoid resolving conflicts with main branch, main will be merged in after to ensure necessary tests are added and branches are up to date.